An Introduction to COM Programming with Delphi

So, what is an Interface?
An interface is the heart of COM technology. When I use the word interface, do not confuse it with the visual display that the user interacts with. It is actually a fairly new reserved word incorporated into Delphi. An Interface is little more than group of abstract methods used to manipulate an object.

We established during the last lesson that an interface is not a class; it is an interface. Therefore, we must declare it as such. Let us explore some interface characteristics.

* An interface is defined as type interface
* You are to use the letter I to define your interface. I.E. "IMyInterface = interface"
* An interface inherits from IUnknown
* You cannot create an instance of an interface like you can a class that contains abstract methods
* An interface cannot contain variables. (Read: It can contain properties as I will show you later on)
* All functions and procedures declared in an interface are public virtual abstract methods. Even though you do not declare them this way, it is understood that is what they are.
* Interfaces are reference counted

Now that we know more about what an interface can and cannot do, let us look at what it is like to declare an interface.

IMyInterface = interface
['{676C8DA0-D8B3-11D4-BDE0-00A024BAF736}']
procedure SomeUnimplementedMethod;
end;

On GUIDs
I am sure you are wondering what the big string of crazy characters are. It is called a GUID and it stands for Globally Unique Identifier. Some people pronounce it as "gwid" or as "Gooey I.D.". Here some characteristics of a GUID:

* Your interface will have a GUID unlike any other in the world
* All interfaces and COM interfaces will have a GUID attached to it
* You can create a GUID in Delphi by pressing CTRL-SHIFT-G
* You are never to copy a GUID from one interface into another

Okay, but how do I implement an interface?
That is a good question. The answer is: You cannot implement an interface directly. You must create a class that will implement your interface. Refer to the coding example shown below.

unit InterfaceUnit;

interface

uses
Windows, Messages, SysUtils, Classes,
Graphics, Controls, Forms, Dialogs, StdCtrls;

type
IMyInterface = interface
['{6675C5C0-D95C-11D4-BDE0-00A024BAF736}']
procedure DisplaySomething;
end;

TMyClass = class(TInterfacedObject, IMyInterface)
procedure DisplaySomething;
end;

TMyForm = class(TForm)
TestBtn: TButton;
procedure TestBtnClick(Sender: TObject);
end;

var
MyForm: TMyForm;

implementation

{$R *.DFM}
procedure TMyForm.TestBtnClick(Sender: TObject);
var
//Declare a TMyClass object
MyClass : TMyClass;
//Declare a IMyInterface Object
MyInterface : IMyInterface;
begin
//Create an interface pointing
//to a TMyClass object
MyInterface := TMyClass.Create;

//Call TMyClass'es DisplaySomething
//using our interface
MyInterface.DisplaySomething;
end;

{ TMyClass }
procedure TMyClass.DisplaySomething;
begin
ShowMessage('MyInterface was created and
will now fall out of scope!');
end;

end.

The coding example listed above is about as simple as it can get while still having some type of visual functionality. Let's take a closer look at procedure TextBtnClick. Is there something missing? If you haven?t notice, we are not explicitly freeing the interface. This would appear to be a memory leak, but appearances are deceiving. As soon as the interface goes out of scope, Delphi will actually free the interface for you automatically! Interface's declared within a procedure or function will naturally fall out of scope when the procedure ends. Interface's declared within a class or are declared globally will naturally fall out of scope when the object is freed or the program ends.

Now we need to discuss Reference Counting. Every time you retrieve an interface to an object, e.g. IMyInterface := MyClass; Delphi will automatically increment its reference count using IUnknown's _AddRef function. When an interface falls out of scope, Delphi automatically calls IUnknown's ._Release function. If you haven't figured it out yet, IUnkown is an interface as well, and since we already know that an interface cannot implement the methods it defines, then you may be wondering where AddRef and Release come from. The answer is TInterfacedObject!
What in the world is TInterfacedObject?
TInterfacedObject and IUnknown are defined in system.pas. Before I can elaborate on this, we need to at least take a look at the code:

SYSTEM.PAS
Declaration of IUnkown
Declaration of TInterfacedObject and its implementation

IUnknown = interface
['{00000000-0000-0000-C000-000000000046}']
function QueryInterface
(const IID: TGUID; out Obj): HResult; stdcall;
function _AddRef: Integer; stdcall;
function _Release: Integer; stdcall;
end;

TInterfacedObject = class(TObject, IUnknown)
protected
FRefCount: Integer;
function QueryInterface
(const IID: TGUID; out Obj): HResult; stdcall;
function _AddRef: Integer; stdcall;
function _Release: Integer; stdcall;
public
procedure BeforeDestruction; override;
property RefCount: Integer read FRefCount;
end;

procedure TInterfacedObject.BeforeDestruction;
begin
if RefCount <> 0 then Error(reInvalidPtr);
end;

function TInterfacedObject.QueryInterface
(const IID: TGUID; out Obj): HResult;
const
E_NOINTERFACE = $80004002;
begin
if GetInterface(IID, Obj) then
Result := 0 else Result := E_NOINTERFACE;
end;

function TInterfacedObject._AddRef: Integer;
begin
Result := InterlockedIncrement(FRefCount);
end;

function TInterfacedObject._Release: Integer;
begin
Result := InterlockedDecrement(FRefCount);
if Result = 0 then Destroy;
end;

The code may look a little scary, but we are going to walk through this step-by-step since it is crucial that you understand reference counting in order to effectively program in COM. It is obvious from the code above that TInterfacedObject implements the methods defined in IUnkown. When you create a class that uses TInterfacedObject, you are essentially telling that class that it is going to be reference counted if an interface is assigned to it.

Let us look at simple piece of code so that I can describe the process that takes place.

TMyInterfaceClass = class(TInterfacedObject, IMyInterface)
{blah blah blah}
{blah blah blah}
procedure DoThis;
var
MyClass : TMyInterfaceClass;
MyInterface : IMyInterface;
begin
MyClass := TMyInterfaceClass.Create;
MyInterface := MyClass
end;

When you use Direct Assignment to assign MyInterface to MyClass, the method _AddRef gets called automatically by Delphi. This says to MyClass, "Hey, an interface is referencing you! Were going to increment your reference count." If you were to change the aforementioned code to the following:

TMyInterfaceClass = class(TInterfacedObject, IMyInterface)
{blah blah blah}
{blah blah blah}
procedure DoThis;
var
MyClass : TMyInterfaceClass;
MyInterface : IMyInterface;
MyInterface2: IMyInterface;
begin
MyClass := TMyInterfaceClass.Create;
MyInterface := MyClass;
MyInterface2 := MyClass
end;

Notice that I added a new variable called MyInterface2. Since we are now setting 2 different interfaces equal to MyClass, guess what happens to the Myclass?s reference count? It becomes 2 because we now have two interfaces assigned to it!

You may be wondering about the _Release method that is implemented by TInterfacedObject. The _Release method automatically gets called for each interface assigned to a class that falls out of scope. Obviously, at the end of procedure DoThis, the program execution is going to return whence it came, therefore, your local class and interface declarations fall out of the scope in which they were declared. Since we have two interfaces defined, _Release will get called how many times? The answer is a bright shiny "2"! And once our reference count hits zero, then MyClass is automatically freed by Delphi. How nice and clean!

 

Google

 

HOME :: BIMBINGAN TUGAS AKHIR :: ARTIKEL TEKNOLOGI :: ARTIKEL LAINNYA :: LINK PENTING :: BUKU TAMU

Copyright © 2008 Sinau Online.Email :alifahnuha@gmail.com